package com.summer.bilibili.util;

import cn.hutool.core.util.StrUtil;
import com.summer.bilibili.consts.Code;
import com.summer.bilibili.consts.Roles;
import com.summer.bilibili.exception.CustomException;
import org.springframework.lang.Nullable;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import com.summer.bilibili.model.UserToken;

import java.util.Collection;
import java.util.Collections;
import java.util.Objects;

/**
 * Spring Security工具类
 * 核心功能：操作安全上下文、获取用户信息、验证权限/角色
 */
public class SecurityUtils {
    /**
     * 获取当前认证用户的UserToken对象
     *
     * @Return 已登录用户返回UserToken，未登录返回null
     */
    @Nullable
    public static UserToken getContextUserToken () {
        // 1. 从线程局部变量获取当前安全上下文
        Authentication authentication = SecurityContextHolder.getContext ( ).getAuthentication ( );
        // 2. 验证是否为用户名密码认证类型
        if ( authentication instanceof UsernamePasswordAuthenticationToken ) {
            // 3. 提取认证详情（Spring Security标准结构）
            Object details = authentication.getDetails ( );
            // 4. 类型转换后返回自定义用户对象
            if ( details instanceof UserToken ) {
                return (UserToken) details;
            }
        }
        return null;// 匿名用户或未认证
    }

    /**
     * 手动设置当前用户到安全上下文
     * <p>
     * 构建认证令牌并存入SecurityContextHolder
     * <p></p>登录成功后设置用户信息
     */
    public static void setContextUserToken ( UserToken userToken ) {
        // 1. 创建认证令牌（principal=用户凭证，credentials=密码占位）
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken ( userToken , "" );
        // 2. 将自定义用户对象存入详情
        token.setDetails ( userToken );
        // 3. 设置到线程绑定的安全上下文
        SecurityContextHolder.getContext ( ).setAuthentication ( token );
    }

    /**
     * 获取当前用户ID
     *
     * @param throwEx 是否在未登录时抛出异常
     */
    public static Long getContextUserId ( boolean throwEx ) {
        // 未登录且需抛异常时触发
        UserToken user = getContextUserToken ( );
        if ( user == null && throwEx ) {
            throw new AuthenticationCredentialsNotFoundException ( "请先登录" );
        }
        return user != null ? user.getId ( ) : null;
    }

    /**
     * 验证用户ID匹配性
     * 用途：防止用户越权操作他人数据
     *
     * @throws AccessDeniedException 用户ID不匹配时抛出
     */
    public static void checkUserId ( Long targetUserId ) throws AccessDeniedException {
        UserToken user = getContextUserToken ( );
        if ( user == null || !Objects.equals ( user.getId ( ) , targetUserId ) ) {
            throw new AccessDeniedException ( "没有权限" );
        }
    }

    /**
     * 获取当前登录用户的名称 未登录的情况下可能是：anonymousUser
     *
     * @throws AuthenticationCredentialsNotFoundException AuthenticationCredentialsNotFound
     */
    @Deprecated
    public static String getContextUsername () throws AuthenticationCredentialsNotFoundException {
        Authentication authentication = SecurityContextHolder.getContext ( ).getAuthentication ( );
        if ( authentication == null || authentication.getName ( ).equals ( "anonymousUser" ) ) {
            throw new AuthenticationCredentialsNotFoundException ( "" );
        }
        if ( authentication instanceof AnonymousAuthenticationToken ) {
            throw new AuthenticationCredentialsNotFoundException ( "匿名用户" );
        }
        return authentication.getName ( );
    }

    /**
     * 获取用户权限集合从Authentication对象提取GrantedAuthority
     *
     * @return 未登录返回空集合(Collections.emptyList ())
     */
    public static Collection < ? extends GrantedAuthority > getContextUserAuthorities () {
        Authentication authentication = SecurityContextHolder.getContext ( ).getAuthentication ( );
        if ( authentication == null ) {
            return Collections.emptyList ( );
        }
        return authentication.getAuthorities ( );
    }

    /**
     * 验证用户是否拥有指定权限
     * 实现原理：遍历权限集合进行字符串匹配
     *
     * @throws CustomException 权限缺失时抛出带状态码的异常
     */
    public static void checkPermission ( String permission ) {
        if ( StrUtil.isBlank ( permission ) ) {
            return;
        }
        Collection < ? extends GrantedAuthority > authorities = getContextUserAuthorities ( );
        for ( GrantedAuthority authority : authorities ) {
            if ( authority.getAuthority ( ).equals ( permission ) ) {
                return;
            }
        }
        throw new CustomException ( Code.FORBIDDEN );
    }

    /**
     * 解析权限数值（如user.operation.count.10 -> 10）前缀匹配+数值截取
     *
     * @param permission   权限前缀（必须以"."结尾）
     * @param defaultValue 解析失败时的默认值
     */
    public static int getContextUserPower ( String permission , int defaultValue ) {
        if ( !permission.endsWith ( "." ) ) {
            permission += ".";
        }
        for ( GrantedAuthority ga : getContextUserAuthorities ( ) ) {
            if ( ga.getAuthority ( ).startsWith ( permission ) ) {
                try {
                    return Integer.parseInt ( ga.getAuthority ( ).replace ( permission , "" ) );
                } catch (NumberFormatException e) {
                    break;
                }
            }
        }
        return defaultValue;
    }

    /**
     * 判断是否为管理员
     */
    public static boolean isAdminUser () {
        UserToken userToken = getContextUserToken ( );
        if ( userToken == null ) {
            return false;
        }
        return userToken.getUserType ( ) == 1 || hasAnyRole ( Roles.ADMIN );
    }

    /**
     * 验证是否拥有任意指定角色
     * 自动补全"ROLE_"前缀后遍历匹配
     *
     * @return 未指定角色时默认返回true
     */
    public static boolean hasAnyRole ( String... roleNames ) {
        if ( roleNames == null || roleNames.length == 0 ) {
            return true;
        }
        for ( String roleName : roleNames ) {
            if ( !roleName.startsWith ( "ROLE_" ) ) {
                roleName = "ROLE_" + roleName;
            }
            Collection < ? extends GrantedAuthority > authorities = getContextUserAuthorities ( );
            for ( GrantedAuthority authority : authorities ) {
                if ( authority.getAuthority ( ).equals ( roleName ) ) {
                    return true;
                }
            }
        }
        return false;
    }
}
